/**************************************************************************
Copyright (C) 2008-2010 United States Government. All rights reserved. 

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
****************************************************************************/
package rage

import grails.test.*
import groovy.util.slurpersupport.GPathResult
import rage.Feed.FeedType
import rage.GeoParserService.GeoType

import java.awt.geom.Point2D

class FeedParserServiceTests extends GrailsUnitTestCase {
	
	def feedParserService

	String feedTitle = "feed1"
	String feedDesc = "syndication feed description"
	String item1Title = "1"
	String item1Desc = "item 1 is about x"
	String item1Date = "2004-08-19T11:54:37-08:00"
	String item2Title = "2"
	String item2Desc = "item 2 is about y"
	String item2Date = "2004-08-19T11:54:37-08:00"
	
	protected void setUp() {
		super.setUp();

		feedParserService = new FeedParserService();

		//Create mock for GeoParserService
		def geoParserServiceMock = mockFor(GeoParserService);
		geoParserServiceMock.demand.getGeoType(1..1) {String arg1 ->
			return GeoType.SIMPLE;
		}
		geoParserServiceMock.demand.parseGeometry(2..2) {GeoType arg1, GPathResult arg2 ->
			List<Geometry> geoList = []
			return geoList;
		};
		feedParserService.geoParserService = geoParserServiceMock.createMock();
	}

	protected void tearDown() {
		super.tearDown()
	}

	void testAddPlacemark() {
		Placemark p = new Placemark()
		p.name = "title1"
		
		List<Placemark> placemarkList = [p]
		Map<String, List<Placemark>> placemarkMap = ["title1":placemarkList]
		
		feedParserService.addPlacemark(p, placemarkMap)
		
		assertEquals("Unexpected map size", 1, placemarkMap.size())
		assertTrue("Map does not contain expected key", placemarkMap.containsKey("title1"))
		List<Placemark> list = placemarkMap.get("title1")
		assertNotNull("map value not expected to be null", list)
		assertEquals("Unexpected list size", 2, list.size())
	}
	
	void testAddPlacemark_emptyList() {
		Map<String, List<Placemark>> placemarkMap = [:]
		Placemark p = new Placemark()
		p.name = "title1"
		
		feedParserService.addPlacemark(p, placemarkMap)
		
		assertEquals("Unexpected map size", 1, placemarkMap.size())
		assertTrue("Map does not contain expected key", placemarkMap.containsKey("title1"))
		List<Placemark> list = placemarkMap.get("title1")
		assertNotNull("map value not expected to be null", list)
		assertEquals("Unexpected list size", 1, list.size())
		
	}
	
	void testParseFeed_html() {
		String html = "<html><head></head><body>This is not a syndication feed!</body></html>";
		Feed feed = feedParserService.parseFeed(html);
		
		assertNotNull("Feed cannot be null", feed);
		assertEquals("Feed field 'title' does not contain expected value",
			FeedParserService.PARSE_ERROR_TITLE, feed.title)
		assertEquals("Feed field 'description' does not contain expected value",
			FeedParserService.PARSE_ERROR_NO_TYPE_MSG, feed.description)
	}

	void testParseFeed_malformed_xml() {
		String badXml = "<entry1><entry2></entry1></entry2>";
		Feed feed = feedParserService.parseFeed(badXml);
		
		assertNotNull("Feed cannot be null", feed);
		assertEquals("Feed field 'title' does not contain expected value",
			FeedParserService.PARSE_ERROR_TITLE, feed.title)
		assertEquals("Feed field 'description' does not contain expected value",
			FeedParserService.PARSE_ERROR_MALFORMED_XML_MSG, feed.description)
	}

	void testParseFeed_Atom_with_updated_field() {
		String updated = "2005-04-06T20:25:05"
		String atom = "<feed xmlns='http://www.w3.org/2005/Atom'>" +
				"<title>" + feedTitle + "</title><subtitle>" + feedDesc + "</subtitle>" +
				"<entry><title>" + item1Title + "</title><content type='text'>" + item1Desc + "</content><updated>" + item1Date + "</updated></entry>" +
				"<entry><title>" + item2Title + "</title><content type='text'>" + item2Desc + "</content><updated>" + item2Date + "</updated></entry>" +
				"</feed>"

		Feed feed = feedParserService.parseFeed(atom)
		assertFeed(feed, FeedType.ATOM, true, null)
	}

	void testParseFeed_Atom_with_published_field_and_kml_enclosures() {
		String updated = "2005-04-06T20:25:05"
		String kmlEnclsoureUrl = "http://localhost/enclosure.kml"
		String kmlEnclosure  = "<link rel='enclosure' type='application/vnd.google-earth.kml+xml' title='myKml' href='" + kmlEnclsoureUrl + "' />"
		String otherEnclosure  = "<link rel='enclosure' type='text/html' title='notKml' href='http:whatever' />"

		String atom = "<feed xmlns='http://www.w3.org/2005/Atom'>" +
				"<title>" + feedTitle + "</title><subtitle>" + feedDesc + "</subtitle>" +
				"<entry><title>" + item1Title + "</title><content type='text'>" + item1Desc + "</content><updated>" + updated + "</updated><published>" + item1Date + "</published>" + kmlEnclosure + otherEnclosure + "</entry>" +
				"<entry><title>" + item2Title + "</title><content type='text'>" + item2Desc + "</content><updated>" + updated + "</updated><published>" + item2Date + "</published>" + kmlEnclosure + "</entry>" +
				"</feed>"

		Feed feed = feedParserService.parseFeed(atom)
		assertFeed(feed, FeedType.ATOM, true, [myKml:kmlEnclsoureUrl])
	}


	void testParseFeed_Atom_with_content_field() {
		String atom = "<feed xmlns='http://www.w3.org/2005/Atom' xmlns:georss='http://www.georss.org/georss'>" +
				"<title>" + feedTitle + "</title>" + "<subtitle>" + feedDesc + "</subtitle>" +
				"<entry><title>" + item1Title + "</title><content type='text'>" + item1Desc + "</content><georss:point>45.256 -71.92</georss:point></entry>" +
				"<entry><title>" + item2Title + "</title><content type='text'>" + item2Desc + "</content><georss:point>45.256 -71.92</georss:point></entry>" + 
				"</feed>"

		Feed feed = feedParserService.parseFeed(atom)
		assertFeed(feed, FeedType.ATOM, false, null)
	}

	void testParseFeed_Atom_with_summary_field() {
		String atom = "<feed xmlns='http://www.w3.org/2005/Atom'>" +
				"<title>" + feedTitle + "</title>" + "<subtitle>" + feedDesc + "</subtitle>" +
				"<entry><title>" + item1Title + "</title><summary>" + item1Desc + "</summary></entry>" +
				"<entry><title>" + item2Title + "</title><summary>" + item2Desc + "</summary></entry>" + 
				"</feed>"

		Feed feed = feedParserService.parseFeed(atom)
		assertFeed(feed, FeedType.ATOM, false, null)
	}

	void testParseFeed_RSS_1_0_with_dc_date() {
		String RSS_1_0 = "<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' " +
						"xmlns:dc='http://purl.org/dc/elements/1.1/' " +
						"xmlns='http://purl.org/rss/1.0'>" +
					"<channel rdf:about='http://xml.com/xml/news.rss'>" +
					"<title>" + feedTitle + "</title><description>" + feedDesc + "</description>" +
					"</channel>" +
					"<item><title>" + item1Title + "</title><description>" + item1Desc + "</description><dc:date>" + item1Date + "</dc:date></item>" +
					"<item><title>" + item2Title + "</title><description>" + item2Desc + "</description><dc:date>" + item2Date + "</dc:date></item>" +
					"</rdf:RDF>"

		Feed feed = feedParserService.parseFeed(RSS_1_0)
		assertFeed(feed, FeedType.RSS_1_0, true, null)
	}

	void testParseFeed_RSS_1_0_with_modules_content() {
		String RSS_1_0 = "<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' " +
						"xmlns:content='http://purl.org/rss/1.0/modules/content/' " +
						"xmlns='http://purl.org/rss/1.0'>" +
					"<channel rdf:about='http://xml.com/xml/news.rss'>" +
					"<title>" + feedTitle + "</title><description>" + feedDesc + "</description>" +
					"</channel>" +
					"<item><title>" + item1Title + "</title><content:encoded>" + item1Desc + "</content:encoded></item>" +
					"<item><title>" + item2Title + "</title><content:encoded>" + item2Desc + "</content:encoded></item>" +
					"</rdf:RDF>"

		Feed feed = feedParserService.parseFeed(RSS_1_0)
		assertFeed(feed, FeedType.RSS_1_0, false, null)
	}

	void testParseFeed_RSS_2_0_with_modules_content_and_kml_enclosures() {
		String kmlEnclosureUrl = "http://localhost/enclosure.kml"
		String kmlEnclosure  = "<enclosure url='" + kmlEnclosureUrl + "' type='application/vnd.google-earth.kml+xml' />"
		
		String RSS_2_0 = "<rss version='2.0' xmlns:content='http://purl.org/rss/1.0/modules/content/' " +
					"xmlns:georss='http://www.georss.org/georss'><channel>" +
					"<title>" + feedTitle + "</title>" + "<description>" + feedDesc + "</description>" +
					"<item><title>" + item1Title + "</title><content:encoded>" + item1Desc + "</content:encoded>" + kmlEnclosure + "</item>" +
					"<item><title>" + item2Title + "</title><content:encoded>" + item2Desc + "</content:encoded>" + kmlEnclosure + "</item>" +
					"</channel></rss>"

		Feed feed = feedParserService.parseFeed(RSS_2_0)
		assertFeed(feed, FeedType.RSS_2_0, false, [enclosure:kmlEnclosureUrl])
	}

	void testParseFeed_RSS_2_0_with_dc_date() {
		String RSS_2_0 = "<rss version='2.0' xmlns:georss='http://www.georss.org/georss' xmlns:dc='http://purl.org/dc/elements/1.1/'><channel>" +
					"<title>" + feedTitle + "</title>" + "<description>" + feedDesc + "</description>" +
					"<item><title>" + item1Title + "</title><description>" + item1Desc + "</description><dc:date>" + item1Date + "</dc:date></item>" +
					"<item><title>" + item2Title + "</title><description>" + item2Desc + "</description><dc:date>" + item2Date + "</dc:date></item>" +
					"</channel></rss>"

		Feed feed = feedParserService.parseFeed(RSS_2_0)
		assertFeed(feed, FeedType.RSS_2_0, true, null)
	}

	void testParseFeed_RSS_2_0_with_pubdate() {
		String RSS_2_0 = "<rss version='2.0' xmlns:georss='http://www.georss.org/georss'><channel>" +
					"<title>" + feedTitle + "</title>" + "<description>" + feedDesc + "</description>" +
					"<item><title>" + item1Title + "</title><description>" + item1Desc + "</description><pubDate>Thu, 19 Aug 2004 11:54:37 PST</pubDate></item>" +
					"<item><title>" + item2Title + "</title><description>" + item2Desc + "</description><pubDate>Thu, 19 Aug 2004 00:00:00 PST</pubDate></item>" +
					"</channel></rss>"

		//Set up mock DateParserService
		def dateParserServiceMock = mockFor(DateParserService);
		dateParserServiceMock.demand.convertToISO8601(2..2) {String arg1 ->
			return item1Date;
		}
		feedParserService.dateParserService = dateParserServiceMock.createMock();

		Feed feed = feedParserService.parseFeed(RSS_2_0)
		assertFeed(feed, FeedType.RSS_2_0, true, null)
	}

	void testRemoveCDATAWrapper() {
		String cleanValue = feedParserService.removeCDATAWrapper("<![CDATA[Hello World]]>");
		assertEquals("Hello World", cleanValue)
	}

	private assertFeed(Feed feed, FeedType type, boolean hasDate, Map<String, String> enclsoures) {
		
		String expectedItem1Timestamp = null
		String expectedItem2Timestamp = null
		if (hasDate) {
			expectedItem1Timestamp = item1Date
			expectedItem2Timestamp = item2Date
		}

		assertNotNull("Feed cannot be null", feed)
		assertEquals("Feed field 'title' does not contain expected value",
			feedTitle, feed.title)
		assertEquals("Feed field 'description' does not contain expected value",
			feedDesc, feed.description)
		assertEquals("Feed field 'type' does not contain expected value",
			type, feed.type)

		Map<String, List<Placemark>> placemarks = feed.placemarks
		assertNotNull("Feed field 'placemarks' should not be null", placemarks)
		assertEquals("Feed field 'placemarks' contains unexpected value",
			2, placemarks.size())
		
		assertPlacemark(placemarks.get(item1Title).get(0), item1Title, item1Desc, expectedItem1Timestamp, enclsoures);
		assertPlacemark(placemarks.get(item2Title).get(0), item2Title, item2Desc, expectedItem2Timestamp, enclsoures);		
	}

	private assertPlacemark(Placemark placemark, String name, String desc, String timestamp, Map<String, String> enclosures) {
		assertNotNull(placemark);
		assertEquals("Placemark field 'name' does not contain expected value",
			name, placemark.name)
		assertEquals("Placemark field 'description' does not contain expected value",
			desc, placemark.description)
		assertEquals("Placemark field 'timestamp' does not contain expected value",
			timestamp, placemark.timestamp)

		List<Geometry> geometryList = placemark.geometryList
		assertNotNull("placemark.geometryList is not expected to be null", geometryList)
		
		if (null != enclosures) {
			assertNotNull("Placemark KML enclosures not expected to be null", placemark.kmlEnclosures)
			assertEquals("Unexpected map size",
				enclosures.size(), placemark.kmlEnclosures.size())
			Set<String> keySet = enclosures.keySet()
			keySet.each{key->
				assertTrue("kmlEnclosures does not contain expected key: " + key,
					placemark.kmlEnclosures.containsKey(key))
				assertEquals("Unexpected value for key: " + key,
					enclosures.get(key), placemark.kmlEnclosures.get(key))
			}
		}

	}
}
